﻿using LightCAD.MathLib;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LightCAD.Three
{


    public sealed class RenderItem
    {
        public int id;
        public Object3D _object;
        public BufferGeometry geometry;
        public Material material;
        public int groupOrder;
        public int renderOrder;
        public double z;
        public GeometryGroup group;
    }

    public sealed class WebGLRenderList
    {
        public static double painterSortStableD(RenderItem a, RenderItem b)
        {

            if (a.groupOrder != b.groupOrder)
            {

                return a.groupOrder - b.groupOrder;

            }
            else if (a.renderOrder != b.renderOrder)
            {

                return a.renderOrder - b.renderOrder;

            }
            else if (a.material.id != b.material.id)
            {

                return a.material.id - b.material.id;

            }
            else if (a.z != b.z)
            {

                return a.z - b.z;

            }
            else
            {
                return a.id - b.id;
            }

        }

        public static int painterSortStable(RenderItem a, RenderItem b)
        {
            return Math.Sign(painterSortStableD(a, b));
        }

        public static double reversePainterSortStableD(RenderItem a, RenderItem b)
        {

            if (a.groupOrder != b.groupOrder)
            {
                return a.groupOrder - b.groupOrder;
            }
            else if (a.renderOrder != b.renderOrder)
            {
                return a.renderOrder - b.renderOrder;
            }
            else if (a.z != b.z)
            {
                return b.z - a.z;
            }
            else
            {
                return a.id - b.id;
            }

        }

        public static int reversePainterSortStable(RenderItem a, RenderItem b)
        {
            return Math.Sign(reversePainterSortStableD(a, b));
        }



        public readonly ListEx<RenderItem> renderItems = new ListEx<RenderItem>();
        public int renderItemsIndex = 0;
        public readonly ListEx<RenderItem> opaque = new ListEx<RenderItem>();
        public readonly ListEx<RenderItem> transmissive = new ListEx<RenderItem>();
        public readonly ListEx<RenderItem> transparent = new ListEx<RenderItem>();
        public void init()
        {
            renderItemsIndex = 0;
            opaque.Clear();
            transmissive.Clear();
            transparent.Clear();
        }
        public RenderItem getNextRenderItem(Object3D _object, BufferGeometry geometry, Material material, int groupOrder, double z, GeometryGroup group)
        {
            var renderItem = renderItems[renderItemsIndex];
            if (renderItem == null)
            {
                renderItem = new RenderItem
                {
                    id = _object.id,
                    _object = _object,
                    geometry = geometry,
                    material = material,
                    groupOrder = groupOrder,
                    renderOrder = _object.renderOrder,
                    z = z,
                    group = group
                };
                renderItems[renderItemsIndex] = renderItem;
            }
            else
            {
                renderItem.id = _object.id;
                renderItem._object = _object;
                renderItem.geometry = geometry;
                renderItem.material = material;
                renderItem.groupOrder = groupOrder;
                renderItem.renderOrder = _object.renderOrder;
                renderItem.z = z;
                renderItem.group = group;
            }
            renderItemsIndex++;
            return renderItem;
        }
        public void push(Object3D _object, BufferGeometry geometry, Material material, int groupOrder, double z, GeometryGroup group)
        {

            var renderItem = getNextRenderItem(_object, geometry, material, groupOrder, z, group);

            if (material.transmission > 0.0)
            {
                transmissive.Push(renderItem);
            }
            else if (material.transparent)
            {
                if (double.IsNaN(renderItem.z))
                {

                }
                transparent.Push(renderItem);
            }
            else
            {
                opaque.Push(renderItem);
            }
        }
        public void unshift(Object3D _object, BufferGeometry geometry, Material material, int groupOrder, double z, GeometryGroup group)
        {

            var renderItem = getNextRenderItem(_object, geometry, material, groupOrder, z, group);

            if (material.transmission > 0.0)
            {

                transmissive.Unshift(renderItem);

            }
            else if (material.transparent)
            {

                transparent.Unshift(renderItem);

            }
            else
            {

                opaque.Unshift(renderItem);

            }
        }
        public void sort(Comparison<RenderItem> customOpaqueSort, Comparison<RenderItem> customTransparentSort)
        {
            if (opaque.Length > 1) opaque.Sort(customOpaqueSort ?? painterSortStable);
            if (transmissive.Length > 1) transmissive.Sort(customTransparentSort ?? reversePainterSortStable);
            if (transparent.Length > 1) transparent.Sort(customTransparentSort ?? reversePainterSortStable);
        }
        public void finish()
        {
            // Clear references from inactive renderItems in the list
            for (int i = renderItemsIndex, il = renderItems.Length; i < il; i++)
            {
                var renderItem = renderItems[i];
                if (renderItem.id == 0) break;
                renderItem.id = 0;
                renderItem._object = null;
                renderItem.geometry = null;
                renderItem.material = null;
                renderItem.group = null;
            }
        }
    }
    public sealed class WebGLRenderLists : IDispose
    {
        public Dictionary<Object3D, ListEx<WebGLRenderList>> lists = new Dictionary<Object3D, ListEx<WebGLRenderList>>();
        public WebGLRenderList get(Object3D scene, int renderCallDepth)
        {
            var listArray = lists.get(scene);
            WebGLRenderList list;
            if (listArray == null)
            {
                list = new WebGLRenderList();
                lists.set(scene, new ListEx<WebGLRenderList> { list });
            }
            else
            {
                if (renderCallDepth >= listArray.Length)
                {
                    list = new WebGLRenderList();
                    listArray.Push(list);
                }
                else
                {
                    list = listArray[renderCallDepth];
                }
            }
            return list;
        }
        public void dispose()
        {
            lists = new Dictionary<Object3D, ListEx<WebGLRenderList>>();
        }
    }
}